%goal SingleExpression
%goal InteractiveSegment % NewLine
%goal ProgramModule
%goal GrammarModule

ImportBinding = (name : [Identifier]+, alias : Identifier?)
FromImportBinding = (name : Identifier, alias : Identifier?)
WithBinding = (value : Expression, variables : Expression?)
ElseIfBranch = (test : Expression, body : Statement)
SwitchCaseClause = (value : Expression, body : Statement)
ExceptionHandler = (`type` : TypeExpression?, name : Identifier?, body : Statement)

Decorator:
    DottedName (names : [Identifier]+)
    DottedNameCall (names : [Identifier]+, arguments : ArgumentList)

DecoratorList = (items : [Decorator])

FunctionDeclaration = (
    isAsync : bool,
    name : Identifier,
    parameters : ParameterList,
    resultType : TypeExpression?,
    body : Statement,
    decorators : DecoratorList
)

ClassDeclaration = (
    name : Identifier,
    arguments : ArgumentList,
    body : Statement,
    decorators : DecoratorList
)

GrammarDeclaration = (
    name : Identifier,
    arguments : ArgumentList,
    directives : DirectiveDeclaration,
    types : TypeDeclaration,
    syntacticDeclaration : SyntacticDeclaration,
    lexicalDeclaration : LexicalDeclaration,
    decorators : DecoratorList
)

GrammarFragment = (
    directives : DirectiveDeclaration,
    types : TypeDeclaration,
    syntacticDeclaration : SyntacticDeclaration,
    lexicalDeclaration : LexicalDeclaration
)

Statement:
    Pass
    Break
    Continue
    Return (value : Expression?)
    Throw (exception : Expression?, cause : Expression?)
    Assert (test : Expression, message : Expression?)
    Delete (targets : [Expression]+)
    Global (names : [Identifier]+)
    Nonlocal (names : [Identifier]+)
    Import (bindings : [ImportBinding]+)
    FromImport (level : int+?, module : [Identifier], bindings : [FromImportBinding]+)
    FromImportAll (level : int+?, module : [Identifier])
    Let (variable : Identifier, `type` : TypeExpression?, value : Expression)
    Var (variable : Identifier, `type` : TypeExpression?, value : Expression)
    Const (variable : Identifier, `type` : TypeExpression?, value : Expression)
    
    Expr (value : Expression)
    Assign (targets : [Expression], value : Expression)
    AnnotatedAssign (target : Expression, `type` : TypeExpression, value : Expression?, isSingle : bool)

    AddAssign (target : Expression, value : Expression)
    SubtractAssign (target : Expression, value : Expression)
    MultiplyAssign (target : Expression, value : Expression)
    MatrixMultiplyAssign (target : Expression, value : Expression)
    FloorDivideAssign (target : Expression, value : Expression)
    DivideAssign (target : Expression, value : Expression)
    ModuloAssign (target : Expression, value : Expression)
    PowerAssign (target : Expression, value : Expression)
    LeftShiftAssign (target : Expression, value : Expression)
    RightShiftAssign (target : Expression, value : Expression)
    UnsignedRightShiftAssign (target : Expression, value : Expression)
    BitwiseAndAssign (target : Expression, value : Expression)
    BitwiseXorAssign (target : Expression, value : Expression)
    BitwiseOrAssign (target : Expression, value : Expression)

    Sequence (items: [Statement]+)
    Simple (body : Statement)
    Block (body : [Statement])
    Suite (isCompound : bool, body : Statement)

    With (isAsync : bool, bindings : [WithBinding]+, body : Statement)
    For (isAsync : bool, enumerator : Expression, enumerable : Expression, body : Statement, orElse : Statement?)
    While (test : Expression, body : Statement, orElse : Statement?)
    If (test : Expression, body : Statement, elseIfBranches : [ElseIfBranch], orElse : Statement?)
    Try (body : Statement, handlers : [ExceptionHandler], orElse : Statement?, finalBody : Statement?)
    Switch (value : Expression, clauses : [SwitchCaseClause], defaultActions : Statement?)

    Function (declaration : FunctionDeclaration)
    Class (declaration : ClassDeclaration)
    Grammar (declaration : GrammarDeclaration)
    EnumType (declarator : EnumDeclarator)
    StructType (declarator : StructDeclarator)
    UnionType (declarator : UnionDeclarator)
    VisitorType (declarator : VisitorDeclarator)
    InterfaceType (declarator : InterfaceDeclarator)
    ClassType (declarator : ClassDeclarator)

SemanticAction:
    Fast (value : Expression)
    Full (actions : Statement)

ProgramDescription:
    Expression (value : SyntacticAnalysis.Expression)
    Segment (body : [Statement])
    Module (body : [Statement])
    Grammar (body : GrammarFragment)

%%

SingleExpression as ProgramDescription:
    TestExpressionList NewLine* => ProgramDescription.NewExpression($1, @1)

InteractiveSegment as ProgramDescription:
    => ProgramDescription.NewSegment(default([Statement]), @@)
    SequenceStatement => ProgramDescription.NewSegment(Sequence.Create($1), @@)
    CompoundStatement => ProgramDescription.NewSegment(Sequence.Create($1), @@)

ProgramModule as ProgramDescription:
    ProgramModuleItemList => ProgramDescription.NewModule($1, @1)

ProgramModuleItemList as [Statement]:
    => default([Statement])
    ProgramModuleItemList NewLine => $1
    ProgramModuleItemList SimpleStatement => Sequence.Append($1, $2)
    ProgramModuleItemList CompoundStatement => Sequence.Append($1, $2)

GrammarModule as ProgramDescription:
    GrammarFragment => ProgramDescription.NewGrammar($1, @1)

SemanticAction as SemanticAction:
    '=>' TestExpression NewLine => SemanticAction.NewFast($2, @@)
    '=>' SuiteStatement => SemanticAction.NewFull($2, @@)

Statement as Statement:
    SimpleStatement => $1
    CompoundStatement => $1

SequenceStatement as Statement:
    SmallStatement %+ ';' ';'? => Statement.NewSequence($1, @@)

SimpleStatement as Statement:
    SequenceStatement NewLine => Statement.NewSimple($1, @@)

BlockStatement as Statement:
    NewLine Indent Statement+ Unindent => Statement.NewBlock($3, @@)

SuiteStatement as Statement:
    ':' SimpleStatement => Statement.NewSuite(False, $2, @@)
    ':' BlockStatement => Statement.NewSuite(True, $2, @@)

SmallStatement as Statement:
    'pass' => Statement.NewPass(@1)
    'break' => Statement.NewBreak(@1)
    'continue' => Statement.NewContinue(@1)
    'return' => Statement.NewReturn(None, @1)
    'return' TestExpressionList => Statement.NewReturn($2, @@)
    'throw' => Statement.NewThrow(None, None, @1)
    'raise' => Statement.NewThrow(None, None, @1)
    'throw' TestExpression => Statement.NewThrow($2, None, @@)
    'raise' TestExpression => Statement.NewThrow($2, None, @@)
    'throw' TestExpression 'from' TestExpression => Statement.NewThrow($2, $4, @@)
    'raise' TestExpression 'from' TestExpression => Statement.NewThrow($2, $4, @@)
    'assert' TestExpression => Statement.NewAssert($2, None, @@)
    'assert' TestExpression ',' TestExpression => Statement.NewAssert($2, $4, @@)
    'del' BitwiseOrExpression %+ ',' ','? => Statement.NewDelete($2, @@)
    'global' Name %+ ',' => Statement.NewGlobal($2, @@)
    'nonlocal' Name %+ ',' => Statement.NewNonlocal($2, @@)
    'import' ImportBinding %+ ',' => Statement.NewImport($2, @@)
    'from' Name %+ '.' 'import' '*' => Statement.NewFromImportAll(None, $2, @@)
    'from' Name %+ '.' 'import' FromImportBinding %+ ',' => Statement.NewFromImport(None, $2, $4, @@)
    'from' Name %+ '.' 'import' '(' FromImportBinding %+ ',' ','? ')' => Statement.NewFromImport(None, $2, $5, @@)
    'from' DotCounter 'import' '*' => Statement.NewFromImportAll($2, default([Identifier]), @@)
    'from' DotCounter 'import' FromImportBinding %+ ',' => Statement.NewFromImport($2, default([Identifier]), $4, @@)
    'from' DotCounter 'import' '(' FromImportBinding %+ ',' ','? ')' => Statement.NewFromImport($2, default([Identifier]), $5, @@)
    'from' DotCounter Name %+ '.' 'import' '*' => Statement.NewFromImportAll($2, $3, @@)
    'from' DotCounter Name %+ '.' 'import' FromImportBinding %+ ',' => Statement.NewFromImport($2, $3, $5, @@)
    'from' DotCounter Name %+ '.' 'import' '(' FromImportBinding %+ ',' ','? ')' => Statement.NewFromImport($2, $3, $6, @@)
    'let' Name '=' TestExpression => Statement.NewLet($2, None, $4, @@)
    'let' Name ':' TypeExpression '=' TestExpression => Statement.NewLet($2, $4, $6, @@)
    'var' Name '=' TestExpression => Statement.NewVar($2, None, $4, @@)
    'var' Name ':' TypeExpression '=' TestExpression => Statement.NewVar($2, $4, $6, @@)
    'const' Name '=' TestExpression => Statement.NewConst($2, None, $4, @@)
    'const' Name ':' TypeExpression '=' TestExpression => Statement.NewConst($2, $4, $6, @@)

    YieldExpression => Statement.NewExpr($1, @1)
    UnpackExpressionList => Statement.NewExpr($1, @1)

    UnpackExpressionList '=' AssignStatementRhs => Statement.NewAssign(Sequence.Prepend($1, $3.Item1), $3.Item2, @@)
    UnpackExpressionList ':' TypeExpression '=' TestExpression => Statement.NewAnnotatedAssign($1, $3, $5, $1.IsNameReference and not @1.GetSource().StartsWith("("), @@)
    UnpackExpressionList ':' TypeExpression => Statement.NewAnnotatedAssign($1, $3, None, $1.IsNameReference and not @1.GetSource().StartsWith("("), @@)

    UnpackExpressionList '+=' YieldExpression => Statement.NewAddAssign($1, $3, @@)
    UnpackExpressionList '-=' YieldExpression => Statement.NewSubtractAssign($1, $3, @@)
    UnpackExpressionList '*=' YieldExpression => Statement.NewMultiplyAssign($1, $3, @@)
    UnpackExpressionList '@=' YieldExpression => Statement.NewMatrixMultiplyAssign($1, $3, @@)
    UnpackExpressionList '//=' YieldExpression => Statement.NewFloorDivideAssign($1, $3, @@)
    UnpackExpressionList '/=' YieldExpression => Statement.NewDivideAssign($1, $3, @@)
    UnpackExpressionList '%=' YieldExpression => Statement.NewModuloAssign($1, $3, @@)
    UnpackExpressionList '**=' YieldExpression => Statement.NewPowerAssign($1, $3, @@)
    UnpackExpressionList '<<=' YieldExpression => Statement.NewLeftShiftAssign($1, $3, @@)
    UnpackExpressionList '>>=' YieldExpression => Statement.NewRightShiftAssign($1, $3, @@)
    UnpackExpressionList '>>>=' YieldExpression => Statement.NewUnsignedRightShiftAssign($1, $3, @@)
    UnpackExpressionList '&=' YieldExpression => Statement.NewBitwiseAndAssign($1, $3, @@)
    UnpackExpressionList '^=' YieldExpression => Statement.NewBitwiseXorAssign($1, $3, @@)
    UnpackExpressionList '|=' YieldExpression => Statement.NewBitwiseOrAssign($1, $3, @@)

    UnpackExpressionList '+=' TestExpressionList => Statement.NewAddAssign($1, $3, @@)
    UnpackExpressionList '-=' TestExpressionList => Statement.NewSubtractAssign($1, $3, @@)
    UnpackExpressionList '*=' TestExpressionList => Statement.NewMultiplyAssign($1, $3, @@)
    UnpackExpressionList '@=' TestExpressionList => Statement.NewMatrixMultiplyAssign($1, $3, @@)
    UnpackExpressionList '//=' TestExpressionList => Statement.NewFloorDivideAssign($1, $3, @@)
    UnpackExpressionList '/=' TestExpressionList => Statement.NewDivideAssign($1, $3, @@)
    UnpackExpressionList '%=' TestExpressionList => Statement.NewModuloAssign($1, $3, @@)
    UnpackExpressionList '**=' TestExpressionList => Statement.NewPowerAssign($1, $3, @@)
    UnpackExpressionList '<<=' TestExpressionList => Statement.NewLeftShiftAssign($1, $3, @@)
    UnpackExpressionList '>>=' TestExpressionList => Statement.NewRightShiftAssign($1, $3, @@)
    UnpackExpressionList '>>>=' TestExpressionList => Statement.NewUnsignedRightShiftAssign($1, $3, @@)
    UnpackExpressionList '&=' TestExpressionList => Statement.NewBitwiseAndAssign($1, $3, @@)
    UnpackExpressionList '^=' TestExpressionList => Statement.NewBitwiseXorAssign($1, $3, @@)
    UnpackExpressionList '|=' TestExpressionList => Statement.NewBitwiseOrAssign($1, $3, @@)

AssignStatementRhs as ([Expression], Expression):
    YieldExpression => (default([Expression]), $1)
    UnpackExpressionList => (default([Expression]), $1)
    UnpackExpressionList '=' AssignStatementRhs => (Sequence.Prepend($1, $3.Item1), $3.Item2)

CompoundStatement as Statement:
    'with' WithBinding %+ ',' SuiteStatement => Statement.NewWith(False, $2, $3, @@)
    'async' 'with' WithBinding %+ ',' SuiteStatement => Statement.NewWith(True, $3, $4, @@)
    'for' TargetExpressionList 'in' TestExpressionList SuiteStatement => Statement.NewFor(False, $2, $4, $5, None, @@)
    'for' TargetExpressionList 'in' TestExpressionList SuiteStatement 'else' SuiteStatement => Statement.NewFor(False, $2, $4, $5, $7, @@)
    'async' 'for' TargetExpressionList 'in' TestExpressionList SuiteStatement => Statement.NewFor(True, $3, $5, $6, None, @@)
    'async' 'for' TargetExpressionList 'in' TestExpressionList SuiteStatement 'else' SuiteStatement => Statement.NewFor(True, $3, $5, $6, $8, @@)
    'while' TestExpression SuiteStatement => Statement.NewWhile($2, $3, None, @@)
    'while' TestExpression SuiteStatement 'else' SuiteStatement => Statement.NewWhile($2, $3, $5, @@)
    'if' TestExpression SuiteStatement ElseIfBranch* => Statement.NewIf($2, $3, $4, None, @@)
    'if' TestExpression SuiteStatement ElseIfBranch* 'else' SuiteStatement => Statement.NewIf($2, $3, $4, $6, @@)
    'try' SuiteStatement 'finally' SuiteStatement => Statement.NewTry($2, default([ExceptionHandler]), None, $4, @@)
    'try' SuiteStatement ExceptionHandler+ => Statement.NewTry($2, $3, None, None, @@)
    'try' SuiteStatement ExceptionHandler+ 'finally' SuiteStatement => Statement.NewTry($2, $3, None, $5, @@)
    'try' SuiteStatement ExceptionHandler+ 'else' SuiteStatement => Statement.NewTry($2, $3, $5, None, @@)
    'try' SuiteStatement ExceptionHandler+ 'else' SuiteStatement 'finally' SuiteStatement => Statement.NewTry($2, $3, $5, $7, @@)
    'switch' TestExpression ':' NewLine Indent SwitchCaseClause+ Unindent => Statement.NewSwitch($2, $6, None, @@)
    'switch' TestExpression ':' NewLine Indent SwitchCaseClause+ 'default' SuiteStatement Unindent => Statement.NewSwitch($2, $6, $8, @@)
    'switch' TestExpression ':' NewLine Indent 'default' SuiteStatement Unindent => Statement.NewSwitch($2, default([SwitchCaseClause]), $7, @@)

    FunctionDeclaration => Statement.NewFunction($1, @1)
    ClassDeclaration => Statement.NewClass($1, @1)
    GrammarDeclaration => Statement.NewGrammar($1, @1)
    EnumTypeDeclarator => Statement.NewEnumType($1, @1)
    StructTypeDeclarator => Statement.NewStructType($1, @1)
    UnionTypeDeclarator => Statement.NewUnionType($1, @1)
    VisitorTypeDeclarator => Statement.NewVisitorType($1, @1)
    InterfaceDeclarator => Statement.NewInterfaceType($1, @1)
    ClassDeclarator => Statement.NewClassType($1, @1)

DotCounter as int:
    '.' => 1
    '..' => 2
    '...' => 3
    DotCounter '.' => $1 + 1
    DotCounter '..' => $1 + 2
    DotCounter '...' => $1 + 3

ImportBinding as ImportBinding:
    Name %+ '.' => ImportBinding.New($1, None, @1)
    Name %+ '.' 'as' Name => ImportBinding.New($1, $3, @@)
    
FromImportBinding as FromImportBinding:
    Name => FromImportBinding.New($1, None, @1)
    Name 'as' Name => FromImportBinding.New($1, $3, @@)

WithBinding as WithBinding:
    TestExpression => WithBinding.New($1, None, @1)
    TestExpression 'as' BitwiseOrExpression => WithBinding.New($1, $3, @@)

ElseIfBranch as ElseIfBranch:
    'elif' TestExpression SuiteStatement => ElseIfBranch.New($2, $3, @@)
    'else' 'if' TestExpression SuiteStatement => ElseIfBranch.New($3, $4, @@)

SwitchCaseClause as SwitchCaseClause:
    'case' TestExpression SuiteStatement => SwitchCaseClause.New($2, $3, @@)

ExceptionHandler as ExceptionHandler:
    'catch' TypeExpression? SuiteStatement => ExceptionHandler.New($2, None, $3, @@)
    'catch' TypeExpression 'as' Name SuiteStatement => ExceptionHandler.New($2, $4, $5, @@)
    'except' TypeExpression? SuiteStatement => ExceptionHandler.New($2, None, $3, @@)
    'except' TypeExpression 'as' Name SuiteStatement => ExceptionHandler.New($2, $4, $5, @@)

Decorator as Decorator:
    Name %+ '.' => Decorator.NewDottedName($1, @1)
    Name %+ '.' ArgumentList => Decorator.NewDottedNameCall($1, $2, @@)

DecoratorItem as Decorator:
    '@' Decorator NewLine => $2

DecoratorList as DecoratorList:
    DecoratorItem+ => DecoratorList.New($1, @1)

FunctionDeclaration as FunctionDeclaration:
    'def' Name '(' ParameterList ')' '->' TypeExpression SuiteStatement => FunctionDeclaration.New(False, $2, $4, $7, $8, DecoratorList.New(default([Decorator])), @@)
    'def' Name '(' ParameterList ')' SuiteStatement => FunctionDeclaration.New(False, $2, $4, None, $6, DecoratorList.New(default([Decorator])), @@)
    'async' 'def' Name '(' ParameterList ')' '->' TypeExpression SuiteStatement => FunctionDeclaration.New(True, $3, $5, $8, $9, DecoratorList.New(default([Decorator])), @@)
    'async' 'def' Name '(' ParameterList ')' SuiteStatement => FunctionDeclaration.New(True, $3, $5, None, $7, DecoratorList.New(default([Decorator])), @@)

    DecoratorList 'def' Name '(' ParameterList ')' '->' TypeExpression SuiteStatement => FunctionDeclaration.New(False, $3, $5, $8, $9, $1, @@)
    DecoratorList 'def' Name '(' ParameterList ')' SuiteStatement => FunctionDeclaration.New(False, $3, $5, None, $7, $1, @@)
    DecoratorList 'async' 'def' Name '(' ParameterList ')' '->' TypeExpression SuiteStatement => FunctionDeclaration.New(True, $4, $6, $9, $10, $1, @@)
    DecoratorList 'async' 'def' Name '(' ParameterList ')' SuiteStatement => FunctionDeclaration.New(True, $4, $6, None, $8, $1, @@)

ClassDeclaration as ClassDeclaration:
    'class' Name OptionalArgumentList SuiteStatement => ClassDeclaration.New($2, $3, $4, DecoratorList.New(default([Decorator])), @@)
    DecoratorList 'class' Name OptionalArgumentList SuiteStatement => ClassDeclaration.New($3, $4, $5, $1, @@)

GrammarDeclaration as GrammarDeclaration:
    'grammar' Name OptionalArgumentList ':' 'pass' NewLine => GrammarDeclaration.New($2, $3, DirectiveDeclaration.New(default([Directive])), TypeDeclaration.New(default([TypeDeclarator])), SyntacticDeclaration.New(default([SyntacticDeclarator])), LexicalDeclaration.New(default([LexicalDeclarator])), DecoratorList.New(default([Decorator])), @@)
    'grammar' Name OptionalArgumentList ':' NewLine Indent 'pass' NewLine Unindent => GrammarDeclaration.New($2, $3, DirectiveDeclaration.New(default([Directive])), TypeDeclaration.New(default([TypeDeclarator])), SyntacticDeclaration.New(default([SyntacticDeclarator])), LexicalDeclaration.New(default([LexicalDeclarator])), DecoratorList.New(default([Decorator])), @@)
    'grammar' Name OptionalArgumentList ':' NewLine Indent DirectiveDeclaration TypeDeclaration Unindent => GrammarDeclaration.New($2, $3, $7, $8, SyntacticDeclaration.New(default([SyntacticDeclarator])), LexicalDeclaration.New(default([LexicalDeclarator])), DecoratorList.New(default([Decorator])), @@)
    'grammar' Name OptionalArgumentList ':' NewLine Indent DirectiveDeclaration TypeDeclaration '%%' NewLine SyntacticDeclaration Unindent => GrammarDeclaration.New($2, $3, $7, $8, $11, LexicalDeclaration.New(default([LexicalDeclarator])), DecoratorList.New(default([Decorator])), @@)
    'grammar' Name OptionalArgumentList ':' NewLine Indent DirectiveDeclaration TypeDeclaration '%%' NewLine SyntacticDeclaration '%%' NewLine LexicalDeclaration Unindent => GrammarDeclaration.New($2, $3, $7, $8, $11, $14, DecoratorList.New(default([Decorator])), @@)

    DecoratorList 'grammar' Name OptionalArgumentList ':' 'pass' NewLine => GrammarDeclaration.New($3, $4, DirectiveDeclaration.New(default([Directive])), TypeDeclaration.New(default([TypeDeclarator])), SyntacticDeclaration.New(default([SyntacticDeclarator])), LexicalDeclaration.New(default([LexicalDeclarator])), $1, @@)
    DecoratorList 'grammar' Name OptionalArgumentList ':' NewLine Indent 'pass' NewLine Unindent => GrammarDeclaration.New($3, $4, DirectiveDeclaration.New(default([Directive])), TypeDeclaration.New(default([TypeDeclarator])), SyntacticDeclaration.New(default([SyntacticDeclarator])), LexicalDeclaration.New(default([LexicalDeclarator])), $1, @@)
    DecoratorList 'grammar' Name OptionalArgumentList ':' NewLine Indent DirectiveDeclaration TypeDeclaration Unindent => GrammarDeclaration.New($3, $4, $8, $9, SyntacticDeclaration.New(default([SyntacticDeclarator])), LexicalDeclaration.New(default([LexicalDeclarator])), $1, @@)
    DecoratorList 'grammar' Name OptionalArgumentList ':' NewLine Indent DirectiveDeclaration TypeDeclaration '%%' NewLine SyntacticDeclaration Unindent => GrammarDeclaration.New($3, $4, $8, $9, $12, LexicalDeclaration.New(default([LexicalDeclarator])), $1, @@)
    DecoratorList 'grammar' Name OptionalArgumentList ':' NewLine Indent DirectiveDeclaration TypeDeclaration '%%' NewLine SyntacticDeclaration '%%' NewLine LexicalDeclaration Unindent => GrammarDeclaration.New($3, $4, $8, $9, $12, $15, $1, @@)

GrammarFragment as GrammarFragment: 
    DirectiveDeclaration TypeDeclaration => GrammarFragment.New($1, $2, SyntacticDeclaration.New(default([SyntacticDeclarator])), LexicalDeclaration.New(default([LexicalDeclarator])), @@)
    DirectiveDeclaration TypeDeclaration '%%' NewLine SyntacticDeclaration => GrammarFragment.New($1, $2, $5, LexicalDeclaration.New(default([LexicalDeclarator])), @@)
    DirectiveDeclaration TypeDeclaration '%%' NewLine SyntacticDeclaration '%%' NewLine LexicalDeclaration => GrammarFragment.New($1, $2, $5, $8, @@)
